﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Collections.ObjectModel;
using Simhopp.Common;
using System.Windows.Data;
using Server;

namespace Simhopp.Server
{
    /// <summary>
    /// The ClientHandler class provides tools to handle multiple client connections.
    /// </summary>
    public class ClientHandler
    {
        #region FIELD REGION

        private bool isRunning;
        public Presenter Presenter { get; private set; }
        private readonly ObservableCollection<ClientListener> _clientListeners;
        private readonly ListCollectionView _judgeList;
        private readonly ListCollectionView _viewerList;

        #endregion
        #region CONSTRUCTOR REGION
        /// <summary>
        /// Constructor taking arguments.
        /// </summary>
        /// <param name="presenter">A reference to the server presenter.</param>
        public ClientHandler(Presenter presenter)
        {
            _clientListeners = new ObservableCollection<ClientListener>();
            _judgeList = new ListCollectionView(_clientListeners);
            _judgeList.Filter += JudgeFilter;
            _viewerList = new ListCollectionView(_clientListeners);
            _viewerList.Filter += ViewerFilter;
            isRunning = true;
            Presenter = presenter;
        }
        #endregion
        #region PROPERTY REGION
        /// <summary>
        /// Gets a ListCollectionView of the viewers associated with the server.
        /// </summary>
        public ListCollectionView Viewers
        {
            get
            {
                return _viewerList;
            }
        }
        /// <summary>
        /// Gets a ListCollectionView of the judges associated with the server.
        /// </summary>
        public ListCollectionView Judges
        {
            get
            {
                return _judgeList;
            }
        }
        #endregion
        #region METHOD REGION
        /// <summary>
        /// Indicates whether the provided client listener has a user.
        /// </summary>
        /// <param name="o">The client listener to check.</param>
        /// <returns>True if the client listener has a user, otherwise false.</returns>
        public bool JudgeFilter(object o)
        {
            var connection = o as ClientListener;
            if (connection.User != null)
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// Indicates whether the provided client listener doesn't have a user.
        /// </summary>
        /// <param name="o">The client listener to check.</param>
        /// <returns>True if the client listener doesn't have a user, otherwise false.</returns>
        public bool ViewerFilter(object o)
        {
            var connection = o as ClientListener;
            if (connection.User == null)
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// Continuoulsy listens to incoming connection requests and satisfies them if possible.
        /// </summary>
        public void RunServer()
        {
            try
            {
                IPHostEntry host;

                string localIP = "";

                host = Dns.GetHostEntry(Dns.GetHostName());

                foreach (IPAddress ip in host.AddressList)
                {
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                    {
                        localIP = ip.ToString();
                        break;
                    }
                }

                localIP = "0.0.0.0";

                var ipAddress = IPAddress.Parse(localIP);

                


                var myListener = new TcpListener(ipAddress, 123);
                myListener.Start();


                LogConsole.WriteLine(String.Format("The server is running..."));
                LogConsole.WriteLine("The local End point is  :" + myListener.LocalEndpoint);
                LogConsole.WriteLine("Waiting for a connection.....");

                while (isRunning)
                {
                    if (myListener.Pending())
                    {
                        TcpClient tpC = myListener.AcceptTcpClient();
                        var client = new ClientListener(tpC, this);
                        App.Current.Dispatcher.Invoke((Action)delegate
                        {
                            _clientListeners.Add(client);
                        });
                    }
                }
                myListener.Stop();
                foreach (var item in _clientListeners)
                    item.Stop();
            }
            catch (Exception e)
            {
                LogConsole.WriteLine("Error..... " + e.StackTrace);
            }
        }
        /// <summary>
        /// Sets the isRunning field to false, effectively halting the server..
        /// </summary>
        public void Stop()
        {
            isRunning = false;
        }
        /// <summary>
        /// Sends the provided network command to all the clients.
        /// </summary>
        /// <param name="o"></param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void SendObjectToAll(NetworkCommand o)
        {
            foreach (var client in _clientListeners)
                client.SendObject(o);
        }
        /// <summary>
        /// Sends the provided network command to the specific user.
        /// </summary>
        /// <param name="o"></param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void SendObjectToUser(NetworkCommand command, User user)
        {
            var listener = GetListenerForUser(user);
            if (listener != null)
                listener.SendObject(command);
            else
                MyLogger.Error("Could not send object since user = null");
        }
        /// <summary>
        /// Gets the client listener associated with the specified user.
        /// </summary>
        /// <param name="user">The user associated with the desired client listener.</param>
        /// <returns>The associated client listener if found, otherwise null.</returns>
        private ClientListener GetListenerForUser(User user)
        {
            foreach (ClientListener listener in _clientListeners)
                if (listener.User != null && listener.User.Equals(user)) return listener;
            return null;
        }
        /// <summary>
        /// Indicates whether the specified user is logged in.
        /// </summary>
        /// <param name="user">The user to check.</param>
        /// <returns>True if the user is currently logged in, otherwise false.</returns>
        public bool IsUserLoggedIn(User user)
        {
            return GetListenerForUser(user) != null;
        }
        /// <summary>
        /// Removes the specified client listener from the client listener list.
        /// </summary>
        /// <param name="clientListener">The client listener to remove.</param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void DeleteFromClientListeners(ClientListener clientListener)
        {
            try
            {
                App.Current.Dispatcher.Invoke((Action)delegate
                {
                    _clientListeners.Remove(clientListener);
                });
            }
            catch (Exception)
            {
                
            }
            
        }
        #endregion
    }
}
